package com.yin.myproject.thread.listblock;

import com.yin.myproject.thread.spinlock.Lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;

/**
 * @author Eason
 * @create 2018-03-07 16:58
 **/
public class CLHLock implements Lock {
    //两个指针,一个指向自己的Node,一个指向前一个Node
    private ThreadLocal<QNode> myNode;
    private ThreadLocal<QNode> myPreNode;
    //原子变量指向队尾
    private AtomicReference<QNode> tail;

    public CLHLock() {
        tail = new AtomicReference<>(new QNode());
        this.myNode = ThreadLocal.withInitial(QNode::new);
        this.myPreNode = ThreadLocal.withInitial(QNode::new);
    }

    @Override
    public void lock() {
        QNode node = myNode.get();
        node.lock = true;
        //CAS原子操作,保证原子性
        QNode preNode = tail.getAndSet(node);
        myPreNode.set(preNode);
        //volatile变量，能保证锁释放及时通知
        //只对前一个节点的状态自旋,减少缓存一致性流量
        while (preNode.lock) {
        }
    }


    @Override
    public void unlock() {
        QNode node = myNode.get();
        node.lock = false;
        //把myNode指向preNode，目的是保证同一个线程下次还能使用这个锁,因为myNode原来指向的节点有它的后一个节点的preNode引用
        //放置这个线程下次lock时myNode.get获得原来的节点
        myNode.set(myPreNode.get());
    }


    public static class QNode {
        volatile boolean lock;
    }
}
